﻿using System;
using System.Activities;
using System.Activities.Core.Presentation;
using System.Activities.Presentation;
using System.Activities.Presentation.Toolbox;
using System.Activities.Presentation.Validation;
using System.Activities.Presentation.View;
using System.Activities.Statements;
using System.Activities.XamlIntegration;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Activities;
using System.ServiceModel.Activities.Presentation.Factories;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;

namespace CustomWorkflowDesigner
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        private WorkflowDesigner _workflowDesigner;
        private string _fileName;

        public MainWindow()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            /*
             * 2. One thing that is needed is to register the workflow activity designer metadata.
             * It is just a single call but leaving it out means all activities are just 
             * a small collapsed shape and won’t expand.
             */
            new DesignerMetadata().Register();
            
            //End point 2.


            /*
             *  3. The toolbox on the left if another standard WPF control called the ToolboxControl.
             *  Again easy to add to any WPF form.
             *  Add to it ToolboxItemWrapper with the activity type to add and you are good to go.
             *  Dragging controls onto the design surface just works, no extra work required.
             *  In the code below I just scan through all types in a few assemblies and 
             *  if they are valid activities I add them to the toolbox.
             */
            var toolbox = new ToolboxControl();

            // add custom activity to toolbox
            var cat = new ToolboxCategory("Custom activities");
            toolbox.Categories.Add(cat);
            cat.Add(new ToolboxItemWrapper(typeof(MyActivityLibrary.SimpleActivity)));
            cat.Add(new ToolboxItemWrapper(typeof(MyActivityLibrary.CompositeActivity)));
            cat.Add(new ToolboxItemWrapper(typeof(MyActivityLibrary.MyActivity)));

            // add Standard Activities
            var cat2 = new ToolboxCategory("Standard Activities");
            var assemblies = new List<Assembly>();
            assemblies.Add(typeof(Send).Assembly);
            assemblies.Add(typeof(Delay).Assembly);
            assemblies.Add(typeof(ReceiveAndSendReplyFactory).Assembly);

            var query = from asm in assemblies
                        from type in asm.GetTypes()
                        where type.IsPublic &&
                        !type.IsNested &&
                        !type.IsAbstract &&
                        !type.ContainsGenericParameters &&
                        (typeof(Activity).IsAssignableFrom(type) ||
                        typeof(IActivityTemplateFactory).IsAssignableFrom(type))
                        orderby type.Name
                        select new ToolboxItemWrapper(type);

            query.ToList().ForEach(ti => cat2.Add(ti));

            toolbox.Categories.Add(cat2);

            Grid.SetColumn(toolbox, 0);
            Grid.SetRow(toolbox, 1);
            LayoutGrid.Children.Add(toolbox);

            //End definition toolbox (point 3)
        }

        private void OpenButton_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new OpenFileDialog();
            if (dlg.ShowDialog() == true)
            {
                _fileName = dlg.FileName;
                ClearWorkflowDesigner();
                LoadWorkflowDesigner();
            }
        }

        private void ClearWorkflowDesigner()
        {
            if (_workflowDesigner != null)
            {
                LayoutGrid.Children.Remove(_workflowDesigner.View);
                LayoutGrid.Children.Remove(_workflowDesigner.PropertyInspectorView);
                _workflowDesigner = null;
            }
        }

        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            if (_workflowDesigner != null && !string.IsNullOrEmpty(_fileName))
            {
                _workflowDesigner.Save(_fileName);
            }
        
            Console.Write("HOLAAAAA");
        }

        //6. Running the Workflow
       
        
        private void RunButton_Click(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(_fileName))
            {
                try
                {

                    this.SaveButton_Click(sender, e);//Save before run
                    /* Just to be able to run the workflow I added a bit of code that uses a WorkflowApplication to run the workflow.
                     * Loading it is also easy, just call the ActivityXamlServices.Load() passing in the file 
                     * and it will return a DynamicActivity ready to run.*/
                    var writer = new StringWriter();
                    var workflow = ActivityXamlServices.Load(_fileName);
                    var wa = new WorkflowApplication(workflow);
                    wa.Extensions.Add(writer);
                    wa.Completed = WorkflowCompleted;
                    wa.OnUnhandledException = WorkflowUnhandledException;
                    wa.Run();

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

        private void WorkflowCompleted(WorkflowApplicationCompletedEventArgs e)
        {
            var writer = e.GetInstanceExtensions<StringWriter>().First();
            MessageBox.Show(writer.ToString(), "Workflow Completed");
        }
        private UnhandledExceptionAction WorkflowUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e)
        {
            MessageBox.Show(e.UnhandledException.Message, "Workflow Unhandled Exception");
            return UnhandledExceptionAction.Terminate;
        }

        private void LoadWorkflowDesigner()
        {
            /*
             * 1. The WorkflowDesigner is the main class to work with.
             * This exposes the actual design surface through the View property and 
             * the linked property sheet through the PropertyInspectorView property.
             * Both these properties point to ready to use WPF UIElement’s so, 
             * as long as the host used WPF, adding them to a form is easy. 
             * 
             * And loading or saving a workflow is easy to,
             * all it takes is a Load() and Save() function pointing to a XAML file.
             *              
             */
            _workflowDesigner = new WorkflowDesigner();
            _workflowDesigner.Load(_fileName);

            var view = _workflowDesigner.View;
            Grid.SetColumn(view, 1);
            Grid.SetRow(view, 1);
            LayoutGrid.Children.Add(view);

            var propInspector = _workflowDesigner.PropertyInspectorView;
            Grid.SetColumn(propInspector, 2);
            Grid.SetRow(propInspector, 1);
            LayoutGrid.Children.Add(propInspector);
            
            //End WorkflowDesigner(point 1)

            /*
             * 5. Validating Workflow
             * It would be nice to let the user know if the workflow is valid.
             * Again quite easy to do by adding a IValidationErrorService to the WorkflowDesigner services collection.
             * In this case I added a listbox to the form and let the IValidationErrorService add each error to the ListBox items.
             * No need to call anything, the IValidationErrorService is automatically called as soon as anything in the workflow changes.
             *
             */
            var validationErrorService = new ValidationErrorService(WorkflowErrors.Items);
            _workflowDesigner.Context.Services.Publish<IValidationErrorService>(validationErrorService);

           
            /*
             * 4. At the top of the form I display the currently selected activity and its parents.
             * The WorkflowDesigner has an Items collection with a bunch of useful objects in there.
             * One of these is a Selection object.
             * We could periodically check this Selection but
             * its even easier to subscribe to chances using the Subscribe() function and 
             * passing in a handler that is called whenever the selection changes.
             * Setting up the subscription is another one-liner.
             */
            _workflowDesigner.Context.Items.Subscribe<Selection>(SelectionChanged);
        }

        //The handler itself isn’t very complex either.(pint 4 also)
        private void SelectionChanged(Selection selection)
        {
            var modelItem = selection.PrimarySelection;
            var sb = new StringBuilder();

            while (modelItem != null)
            {
                var displayName = modelItem.Properties["DisplayName"];

                if (displayName != null)
                {
                    if (sb.Length > 0)
                        sb.Insert(0, " - ");
                    sb.Insert(0, displayName.ComputedValue);
                }

                modelItem = modelItem.Parent;
            }

            CurrentActivityName.Text = sb.ToString();
            // End of The Current Selection (Point 4)
        }
        
    }
}